package com.hyk.commonLib.common.utils;

import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.CharacterStyle;
import android.text.style.ForegroundColorSpan;
import android.text.style.ImageSpan;
import android.text.style.ReplacementSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.StyleSpan;
import android.text.style.UnderlineSpan;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.hyk.commonLib.R;

import org.jetbrains.annotations.NotNull;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.Locale;

/**
 * Created by 贺玉琨 on 2019/6/3.
 */
public class TextEffectUtils {

    @IntDef({SCALE_TYPE_NONE, SCALE_TYPE_SQUARE, SCALE_TYPE_FIT_WIDTH, SCALE_TYPE_FIT_HEIGHT})
    @Retention(RetentionPolicy.SOURCE)
    public @interface ScaleType {
    }

    public static final int SCALE_TYPE_NONE = 0;
    public static final int SCALE_TYPE_SQUARE = 1;
    public static final int SCALE_TYPE_FIT_WIDTH = 2;
    public static final int SCALE_TYPE_FIT_HEIGHT = 3;

    public enum Effect {
        BOLD(new StyleSpan(Typeface.BOLD)),                  //加粗
        ITALIC(new StyleSpan(Typeface.ITALIC)),              //斜体
        BOLD_ITALIC(new StyleSpan(Typeface.BOLD_ITALIC)),    //加粗+斜体
        UNDERLINE(new UnderlineSpan()),                      //下划线
        STRIKETHROUGH(new StrikethroughSpan());              //删除线

        CharacterStyle style;

        Effect(CharacterStyle style) {
            this.style = style;
        }
    }

    public enum Size {
        S(ResUtils.getDimension(R.dimen.text_size_s)),
        M(ResUtils.getDimension(R.dimen.text_size_m)),
        X(ResUtils.getDimension(R.dimen.text_size_x)),
        XX(ResUtils.getDimension(R.dimen.text_size_xx)),
        XXX(ResUtils.getDimension(R.dimen.text_size_xxx)),
        XXXX(ResUtils.getDimension(R.dimen.text_size_xxxx)),
        XXXXX(ResUtils.getDimension(R.dimen.text_size_xxxxx)),
        XXXXXX(ResUtils.getDimension(R.dimen.text_size_xxxxxx));

        int size;

        Size(float size) {
            this.size = (int) size;
        }
    }

    private final SpannableStringBuilder builder;
    private CharSequence lastString;

    private TextEffectUtils() {
        builder = new SpannableStringBuilder();
    }

    private TextEffectUtils(CharSequence s) {
        if (s == null) s = "";
        builder = new SpannableStringBuilder(s);
        lastString = s;
    }

    private TextEffectUtils(SpannableStringBuilder builder) {
        this.builder = builder;
        lastString = builder.toString();
    }

    /**
     * 获取对象
     */
    public static TextEffectUtils from() {
        return new TextEffectUtils();
    }

    /**
     * 获取对象
     */
    public static TextEffectUtils from(CharSequence s) {
        return new TextEffectUtils(s);
    }

    /**
     * 获取对象
     */
    public static TextEffectUtils from(SpannableStringBuilder builder) {
        return new TextEffectUtils(builder);
    }

    /**
     * 添加普通文字
     */
    public TextEffectUtils add(CharSequence s) {
        return append(s);
    }

    /**
     * 添加自定义样式文字
     */
    public TextEffectUtils add(SpannableStringBuilder builder) {
        return append(builder);
    }

    /**
     * 添加自定义颜色的文字
     */
    public TextEffectUtils add(CharSequence s, @ColorInt int color) {
        return append(s, new ForegroundColorSpan(color));
    }

    /**
     * 添加自定义颜色的文字
     */
    public TextEffectUtils add(CharSequence s, String color) {
        return add(s, Color.parseColor(color));
    }

    /**
     * 添加可点击文字
     */
    public TextEffectUtils add(CharSequence s, View.OnClickListener onClickListener) {
        return add(s, onClickListener, ClickableSpan.DEFAULT_COLOR);
    }

    /**
     * 添加可点击文字_自定义颜色
     */
    public TextEffectUtils add(CharSequence s, View.OnClickListener onClickListener, int color) {
        return add(s, onClickListener, color, ClickableSpan.DEFAULT_SHOW_UNDERLINE);
    }

    /**
     * 添加可点击文字_自定义显示下划线
     */
    public TextEffectUtils add(CharSequence s, View.OnClickListener onClickListener, boolean showUnderline) {
        return add(s, onClickListener, ClickableSpan.DEFAULT_COLOR, showUnderline);
    }

    /**
     * 添加可点击文字_自定义颜色_自定义显示下划线
     */
    public TextEffectUtils add(CharSequence s, View.OnClickListener onClickListener, int color, boolean showUnderline) {
        ClickableSpan clickableSpan = new ClickableSpan(onClickListener, color, showUnderline);
        return append(s, clickableSpan);
    }

    /**
     * 添加文字_加粗、斜体、加粗+斜体、下划线、删除线
     */
    public TextEffectUtils add(CharSequence s, Effect effect) {
        return append(s, effect.style);
    }

    /**
     * 添加自定义大小的文字
     */
    public TextEffectUtils add(CharSequence s, int size, boolean inDp) {
        return append(s, new AbsoluteSizeSpan(size, inDp));
    }

    /**
     * 添加自定义大小的文字
     */
    public TextEffectUtils add(CharSequence s, Size size) {
        return append(s, new AbsoluteSizeSpan(size.size, false));
    }

    /**
     * 添加图片
     */
    public TextEffectUtils add(@DrawableRes int drawableRes) {
        return add(drawableRes, -1, SCALE_TYPE_NONE, false);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(@DrawableRes int drawableRes, int size) {
        return add(drawableRes, size, SCALE_TYPE_SQUARE, true);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(@DrawableRes int drawableRes, int size, boolean dpSize) {
        return add(drawableRes, size, SCALE_TYPE_SQUARE, dpSize);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(@DrawableRes int drawableRes, int size, @ScaleType int scaleType) {
        return add(drawableRes, size, scaleType, true);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(Drawable drawable, int size) {
        return add(drawable, size, SCALE_TYPE_SQUARE, true);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(Drawable drawable, int size, boolean dpSize) {
        return add(drawable, size, SCALE_TYPE_SQUARE, dpSize);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(Drawable drawable, int size, @ScaleType int scaleType) {
        return add(drawable, size, scaleType, true);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(@DrawableRes int drawableRes, int size, @ScaleType int scaleType, boolean dpSize) {
        return add(ResUtils.getDrawable(drawableRes), size, scaleType, dpSize);
    }

    /**
     * 添加自定义大小的图片
     */
    public TextEffectUtils add(Drawable drawable, int size, @ScaleType int scaleType, boolean dpSize) {
        if (drawable == null) return this;
        int sizeInPx = dpSize ? ScreenUtils.dp2px(size) : size;
        int width;
        int height;
        switch (scaleType) {
            case SCALE_TYPE_FIT_HEIGHT:
                height = sizeInPx;
                width = (int) (drawable.getIntrinsicWidth() / (float) drawable.getIntrinsicHeight() * height);
                break;
            case SCALE_TYPE_FIT_WIDTH:
                width = sizeInPx;
                height = (int) (drawable.getIntrinsicHeight() / (float) drawable.getIntrinsicWidth() * width);
                break;
            case SCALE_TYPE_SQUARE:
                width = sizeInPx;
                height = sizeInPx;
                break;
            case SCALE_TYPE_NONE:
            default:
                width = drawable.getIntrinsicWidth();
                height = drawable.getIntrinsicHeight();
                break;
        }
        drawable.setBounds(0, 0, width, height);
        return append("img", new CenterImageSpan(drawable));
    }

    /**
     * 回车
     */
    public TextEffectUtils addNewLine() {
        return addNewLine(1);
    }

    /**
     * 多行回车
     */
    public TextEffectUtils addNewLine(int n) {
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < n; i++) {
            str.append("\n");
        }
        append(str);
        return this;
    }

    /**
     * 空格
     */
    public TextEffectUtils addSpace() {
        return addSpace(1);
    }

    /**
     * 多个空格
     */
    public TextEffectUtils addSpace(@IntRange(from = 1) int n) {
        append(String.format(Locale.CHINESE, "%" + n + "c", ' '));
        return this;
    }

    public TextEffectUtils addMargin(float margin) {
        return addMargin(margin, true);
    }

    public TextEffectUtils addMargin(float margin, boolean dpSize) {
        return append("margin", new MarginSpan(dpSize ? ScreenUtils.dp2px(margin) : (int) margin));
    }

    /**
     * 设置到textView中
     */
    public void into(TextView textView) {
        if (builder.getSpans(0, builder.length(), ClickableSpan.class).length != 0) {
            textView.setMovementMethod(LinkMovementClickMethod.getInstance());
        } else {
            try {
//                Method method = textView.getClass().getDeclaredMethod("getDefaultMovementMethod");
                Method method = getDefaultMovementMethod(textView.getClass());
                if (method != null) {
                    method.setAccessible(true);
                    textView.setMovementMethod((MovementMethod) method.invoke(textView));
                    method.setAccessible(false);
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        textView.setText(builder);
    }

    private Method getDefaultMovementMethod(Class<?> clazz) {
        while (clazz != null) {
            Method[] methods = clazz.getDeclaredMethods();
            for (Method m : methods) {
                if ("getDefaultMovementMethod".equals(m.getName()) && m.getParameterTypes().length == 0) {
                    return m;
                }
            }
            clazz = clazz.getSuperclass();
        }
        return null;
    }

    /**
     * 获取文字对象
     */
    public SpannableStringBuilder get() {
        return builder;
    }

    /**
     * 为上一次的文字添加效果
     */
    public TextEffectUtils addEffect(CharacterStyle what) {
        if (TextUtils.isEmpty(lastString)) return this;
        int start = builder.length() - lastString.length();
        builder.setSpan(what, start, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        return this;
    }

    /**
     * 为上一次的文字添加效果
     */
    public TextEffectUtils addEffect(Effect effect) {
        if (TextUtils.isEmpty(lastString)) return this;
        int start = builder.length() - lastString.length();
        builder.setSpan(effect.style, start, builder.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        return this;
    }

    /**
     * 为上一次的文字添加效果
     */
    public TextEffectUtils addEffect(int color) {
        return addEffect(new ForegroundColorSpan(color));
    }

    /**
     * 为上一次的文字添加效果
     */
    public TextEffectUtils addEffect(Size size) {
        return addEffect(new AbsoluteSizeSpan(size.size, false));
    }

    /**
     * 为上一次的文字添加效果
     */
    public TextEffectUtils centerVertical() {
        return addEffect(new VerticalCenterSpan());
    }

    /**
     * 为上一次的文字添加效果
     */
    public TextEffectUtils addEffect(View.OnClickListener onClickListener) {
        ClickableSpan clickableSpan = new ClickableSpan(onClickListener, ClickableSpan.DEFAULT_COLOR, ClickableSpan.DEFAULT_SHOW_UNDERLINE);
        return addEffect(clickableSpan);
    }

    private TextEffectUtils append(CharSequence s) {
        if (TextUtils.isEmpty(s)) return this;
        lastString = s;
        builder.append(s);
        return this;
    }

    private TextEffectUtils append(SpannableStringBuilder builder) {
        if (builder.length() == 0) return this;
        lastString = builder.toString();
        builder.append(lastString);
        return this;
    }

    private TextEffectUtils append(CharSequence s, CharacterStyle what) {
        if (TextUtils.isEmpty(s)) return this;
        append(s).addEffect(what);
        return this;
    }

    private static class ClickableSpan extends android.text.style.ClickableSpan {

        static final int DEFAULT_COLOR = Color.CYAN;
        static final boolean DEFAULT_SHOW_UNDERLINE = true;

        View.OnClickListener onClickListener;
        int color;
        boolean showUnderline;

        ClickableSpan(View.OnClickListener onClickListener, int color, boolean showUnderline) {
            this.onClickListener = onClickListener;
            this.color = color;
            this.showUnderline = showUnderline;
        }

        @Override
        public void onClick(@NonNull View widget) {
            if (onClickListener != null) onClickListener.onClick(widget);
        }

        @Override
        public void updateDrawState(@NonNull TextPaint ds) {
            ds.setColor(color);
            ds.setUnderlineText(showUnderline);
        }
    }

    private static class LinkMovementClickMethod extends LinkMovementMethod {

        private long lastClickTime;

        private static final long CLICK_DELAY = 500L;

        @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
            int action = event.getAction();

            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
                int x = (int) event.getX();
                int y = (int) event.getY();

                x -= widget.getTotalPaddingLeft();
                y -= widget.getTotalPaddingTop();

                x += widget.getScrollX();
                y += widget.getScrollY();

                Layout layout = widget.getLayout();
                int line = layout.getLineForVertical(y);
                int off = layout.getOffsetForHorizontal(line, x);

                ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);

                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        if (System.currentTimeMillis() - lastClickTime < CLICK_DELAY) {
                            link[0].onClick(widget);
                        }
                    } else {
                        Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));
                        lastClickTime = System.currentTimeMillis();
                    }
                    return true;
                } else {
                    Selection.removeSelection(buffer);
                }
            }
            return super.onTouchEvent(widget, buffer, event);
        }

        public static LinkMovementClickMethod getInstance() {
            if (null == sInstance) {
                sInstance = new LinkMovementClickMethod();
            }
            return sInstance;
        }

        private static LinkMovementClickMethod sInstance;

    }

    public static class CenterImageSpan extends ImageSpan {
        public CenterImageSpan(@NonNull Drawable drawable) {
            super(drawable);
        }

        public int getSize(@NotNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            Drawable d = getDrawable();
            Rect rect = d.getBounds();
            if (fm != null) {
                Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
                int fontHeight = fmPaint.bottom - fmPaint.top;
                int drHeight = rect.bottom - rect.top;

                int top = drHeight / 2 - fontHeight / 4;
                int bottom = drHeight / 2 + fontHeight / 4;

                fm.ascent = -bottom;
                fm.top = -bottom;
                fm.bottom = top;
                fm.descent = top;
            }
            return rect.right;
        }

        @Override
        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NotNull Paint paint) {
            Drawable b = getDrawable();
            canvas.save();
            int transY = ((bottom - top) - b.getBounds().bottom) / 2 + top;
            canvas.translate(x, transY);
            b.draw(canvas);
            canvas.restore();
        }
    }

    /**
     * 使TextView中不同大小字体垂直居中
     */
    public static class VerticalCenterSpan extends ReplacementSpan {

        @Override
        public int getSize(@NotNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
            text = text.subSequence(start, end);
            return (int) paint.measureText(text.toString());
        }

        @Override
        public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NotNull Paint paint) {
            text = text.subSequence(start, end);
            Paint.FontMetricsInt fm = paint.getFontMetricsInt();
            // 此处重新计算y坐标，使字体居中
            canvas.drawText(text.toString(), x, y - ((y + fm.descent + y + fm.ascent) / 2F - (bottom + top) / 2F), paint);
        }
    }

    private static class MarginSpan extends ReplacementSpan {

        private final int margin;

        private MarginSpan(int margin) {
            this.margin = margin;
        }

        @Override
        public int getSize(@NonNull Paint paint, CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, @Nullable Paint.FontMetricsInt fm) {
            return margin;
        }

        @Override
        public void draw(@NonNull Canvas canvas, CharSequence text, @IntRange(from = 0) int start, @IntRange(from = 0) int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
        }
    }

}